﻿using System.Collections.Generic;

namespace System {

    public interface IRandom {

        int Next(int max);
    }

    public interface IWeightedRandomSelector<TKey> : IObject {

        void Add(TKey key, int weight);

        void Initialize(IRandom random);

        TKey Random();

        void Remove(TKey key);
    }

    [Implement(typeof(IWeightedRandomSelector<>))]
    internal sealed class KHBD_WeightedRandomSelector<TKey> : KHBD_Object, IWeightedRandomSelector<TKey> {

        private Dictionary<TKey, int> m_WeightDictionary;
        private IRandom m_Random;
        private bool m_IsDirty;

        private int m_CachedTotalWeight;

        [Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:命名样式", Justification = "<挂起>")]
        private int m_TotalWeight {
            get {
                if (m_IsDirty) {
                    m_IsDirty = false;
                    m_CachedTotalWeight = 0;
                    foreach (var kv in m_WeightDictionary) {
                        m_CachedTotalWeight += kv.Value;
                    }
                }
                return m_CachedTotalWeight;
            }
        }

        protected override void Dispose() {
            m_WeightDictionary = null;
            m_Random = null;
        }

        public KHBD_WeightedRandomSelector() {
            m_WeightDictionary = new Dictionary<TKey, int>();
            m_IsDirty = true;
        }

        public void Initialize(IRandom random) {
            m_Random = random;
        }

        public void Add(TKey key, int weight) {
            m_WeightDictionary.Add(key, weight);
            m_IsDirty = true;
        }

        public void Remove(TKey key) {
            if (m_WeightDictionary.Remove(key)) {
                m_IsDirty = true;
            }
        }

        public TKey Random() {
            int random = m_Random.Next(m_TotalWeight);
            int cumulativeWeight = 0;
            foreach (var kv in m_WeightDictionary) {
                cumulativeWeight += kv.Value;
                if (random < cumulativeWeight) {
                    return kv.Key;
                }
            }
            return default;
        }
    }
}